package ltd.newbee.mall.config.aop;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import ltd.newbee.mall.config.CurrentAdminUser;
import ltd.newbee.mall.config.annotation.LogOperate;
import ltd.newbee.mall.entity.AdminLogOperate;
import ltd.newbee.mall.entity.AdminUser;
import ltd.newbee.mall.service.AdminLogOperateService;
import ltd.newbee.mall.util.AddressUtils;
import ltd.newbee.mall.util.IpUtils;
import ltd.newbee.mall.util.SpringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * @description: 操作日志
 * @author: qzx
 * @date: 2022-08-12 14:51
 * @version: 1.0
 */
@Component
@Aspect
@Order(9)
@Slf4j
public class LogAspect {

    private Date startTime;

    @Resource
    private CurrentAdminUser currentAdminUser;

    @Resource
    private AdminLogOperateService adminLogOperateService;

    @Pointcut("@annotation(ltd.newbee.mall.config.annotation.LogOperate)")
    public void logPointCut() {

    }

    /***
     * 开始写入之前
     * @param point
     */
    @Before(value = "logPointCut()")
    public void startOpt(JoinPoint point) {
        this.startTime = new Date();
        log.info("==== 开始时间 startTime = {}", DateUtil.format(this.startTime, "yyyy-MM-dd HH:mm:ss"));
    }

    /***
     * 处理完成之后
     * @param joinPoint
     * @param value
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "value")
    public void writeLog(JoinPoint joinPoint, Object value) {
        LogOperate logOperate = this.getAnnotationLog(joinPoint);
        if (logOperate != null) {
            this.handleLog(joinPoint, null, value, logOperate);
        }
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        LogOperate logOperate = this.getAnnotationLog(joinPoint);
        if (logOperate != null) {
            this.handleLog(joinPoint, e, null, logOperate);
        }
    }

    private LogOperate getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(LogOperate.class);
        }
        return null;
    }

    @Async
    public void handleLog(JoinPoint point, Exception e, Object result, LogOperate logOperate) {
        try {
            AdminLogOperate operate = new AdminLogOperate();
            operate.setStartTime(this.startTime);
            operate.setEndTime(DateUtil.date());
            AdminUser user = currentAdminUser.currentLoginUser();
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            String className = point.getTarget().getClass().getName();
            String methodName = method.getName();
            operate.setClassName(className);
            operate.setMethodName(methodName);
            operate.setTitle(logOperate.title());
            operate.setUserId(user.getAdminUserId());
            if (ObjectUtil.isNotEmpty(result)) {
                operate.setJsonResult(JSONObject.toJSONString(result));
            }
            if (ObjectUtil.isNotEmpty(e)) {
                operate.setErrorMsg(e.getMessage());
            }
            HttpServletRequest request = SpringUtils.currentRequest();
            String ip = IpUtils.getIpAddr(request);
            operate.setIp(ip);
            String address = AddressUtils.getRealAddressByIP(ip);
            operate.setAddress(address);
            operate.setRequestMethod(request.getMethod());
            if (!request.getMethod().equalsIgnoreCase("get")) {
                String params = StreamUtils.copyToString(request.getInputStream(), CharsetUtil.CHARSET_UTF_8);
                operate.setRequestParams(params);
            } else {
                String params = request.getQueryString();
                operate.setRequestParams(params);
            }
            adminLogOperateService.save(operate);
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error("写入异常 LogAspect --> handleLog = {}", ex.getMessage());
        }

    }
}
